package com.shelpe.services.simbasync.controller;



import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shelpe.services.simbasync.dto.ItemsDto;
import com.shelpe.services.simbasync.exception.ApiException;
import com.shelpe.services.simbasync.exception.RequestParamException;
import com.shelpe.services.simbasync.model.Products;
import com.shelpe.services.simbasync.model.Users;
import com.shelpe.services.simbasync.service.ProductsService;
import com.shelpe.services.simbasync.service.UserService;
import com.shelpe.services.simbasync.service.simbaproxy.ProductsProxy;
import com.shelpe.services.simbasync.util.Util;


@RestController
@RequestMapping(value="/simbasync")
public class ProductsController {
	
	private static final Logger logger = LoggerFactory.getLogger(ProductsController.class);
	
	private static final Map<String, Date> requestPool = new HashMap<String, Date>();
	
	@Autowired
	private ProductsProxy proxy;
	
	@Autowired
	private ProductsService service;
    
    @Autowired
    private UserService user;
    
    private final ExecutorService executor = Executors.newFixedThreadPool(4);
    
    @RequestMapping("/tenants/{user}/@product")
    @ResponseBody
	public ItemsDto syncProductsByUser(@PathVariable("user") String user){
    	
    	boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? syncProducts(null, Long.valueOf(user)) : syncProducts(user, null);
    }
    
    @RequestMapping("/tenants/{user}/@productneedsync")
    @ResponseBody
	public ItemsDto needSyncProductsByUser(@PathVariable("user") String user){
    	
    	boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		
		return isNumber ? needSyncProducts(null, Long.valueOf(user)) : needSyncProducts(user, null);
    }
    
    @RequestMapping("/tenants/{user}/@productasyn")
	@ResponseBody
	public DeferredResult<ItemsDto> syncProductsByUserAsyn(@PathVariable("user") final String user){
		
		final DeferredResult<ItemsDto> result = new DeferredResult<ItemsDto>();
		final boolean isNumber = user.matches("-?[0-9]+.*[0-9]*");
		CompletionService<ItemsDto> comletionService = new ExecutorCompletionService<ItemsDto>(executor); 
		
		comletionService.submit(new Callable<ItemsDto>(){
                                                                                                                                                                                                    
			@Override
			public ItemsDto call() throws Exception {
				// TODO Auto-generated method stub
				ItemsDto dto = null;
				if(isNumber){
					dto = syncProducts(null, Long.valueOf(user));
				}else{
					dto = syncProducts(user, null);
				}
				
				result.setResult(dto);
				
				return dto;
			}});
		
		return result;
	}
    
    private ItemsDto needSyncProducts(String nick, Long userId){
    	
		if(userId == null){
			userId = user.getUserIdByNick(nick);
		}
		
		if(nick == null){
			nick = user.getNickByUserId(userId);
		}
		
		if(nick == null || userId == null){
			logger.warn("nick or userId is null, do nothing");
			throw new RequestParamException();
		}
		
		ItemsDto dto = new ItemsDto();
		dto.setNick(nick);
		dto.setUserId(userId);
		dto.setProcess("needSyncProducts");
		Date beginProcessTime = new Date();
		
		List<Products> items = service.getItemsByUserId(userId);
		boolean needSync = true;
		if(items != null && items.size() > 0){
			needSync = false;
		}
		logger.info(">>>needSyncProducts nick {}, userId {}, needSync {}", nick, userId, needSync);
		dto.setNeedSync(needSync);
		Date endProcessTime = new Date();
		long durTime = endProcessTime.getTime() - beginProcessTime.getTime();
		dto.setDurTime(durTime);
		
		return dto;
    }
    
	private ItemsDto syncProducts(String nick, Long userId){
		
		Users u = null;
		
		if(userId == null){
			List<Users> uList = user.getUserByNick(nick);
			if(uList != null && uList.size() > 0){
				u = uList.iterator().next();
				userId = u.getUserId();
			}
		}
		
		if(nick == null){
			u = user.getUserByUserId(userId);
			if(u != null){
				nick = u.getNick();
			}
		}
		
		if(nick == null || userId == null){
			logger.warn("nick or userId is null, do nothing");
			throw new RequestParamException();
		}
		logger.info(">>>syncProducts nick {}, userId {}", nick, userId);
		ItemsDto dto = new ItemsDto();
		dto.setNick(nick);
		dto.setUserId(userId);
		dto.setProcess("syncProducts");
		Date beginProcessTime = new Date();
		String userIdStr = userId.toString();
		
		if(requestPool.containsKey(userIdStr)){
		
			Date lastProcessDate = requestPool.get(userIdStr);
			if(beginProcessTime.getTime() - lastProcessDate.getTime() > Util.MilliSecondsOfMin * 2){
				requestPool.remove(userIdStr);
				logger.info(">>>syncProducts nick {}, userId {} process out time, restart new sync task", nick, userId);
			}else{
				dto.setMessage("On processing");
				logger.info(">>>syncProducts nick {}, userId {} request too freq", nick, userId);
				return dto;
			}
		}

		requestPool.put(userIdStr, beginProcessTime);
		
		int pageSize = 200;
		int pageNo = 1;
		int totalItem = 0;
		int pages = 0;
		String response = null;
		try{
			response = proxy.getOnlineItems(nick, pageSize, pageNo);
		}catch(Exception e){
			logger.warn("Maybe something is wrong or can not find token, userId {}", userId);
		}
		
		if(response != null && response.contains("simba_adgroup_onlineitemsvon_get_response")){
			JSONObject object = JSON.parseObject(response);
			JSONObject objRes = object==null? null : object.getJSONObject("simba_adgroup_onlineitemsvon_get_response");
			JSONObject objItems = objRes==null? null : objRes.getJSONObject("page_item");
			totalItem =  objItems==null ? 0 : objItems.getIntValue("total_item");
			
			pages = totalItem/pageSize;
			if(totalItem%pageSize > 0){
				pages++;
			}
			
		}else{
			logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
			if(response != null && response.contains("isv.invalid-permission")){//没有权限
				dto.setMessage("invalid-permission");
				requestPool.remove(userIdStr);
				return dto;
			}else if(response != null && response.contains("invalid-sessionkey")){
				dto.setMessage("invalid-sessionkey");
				logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
				requestPool.remove(userIdStr);
				return dto;
			}else if(response != null && response.contains("无法根据nick获取直通车帐号信息")){
				dto.setMessage("无法根据nick获取直通车帐号信息");
				logger.warn("can not get any info from proxy, maybe something is wrong, nick is {}, response {}", nick, response);
				requestPool.remove(userIdStr);
				return dto;
			}else if(response == null){
				dto.setMessage("get no information");
				requestPool.remove(userIdStr);
				return dto;
			}else{
				requestPool.remove(userIdStr);
				throw new ApiException();
			}
		}
		
		List<Products> listTotal = new ArrayList<Products>();
		try{
			
			List<Products> subList = service.parseJsonToProducts(response);
			if(subList != null && subList.size() > 0){
				listTotal.addAll(subList);
			}
			
			for(int i = 2; i <= pages; i++){
				response = proxy.getOnlineItems(nick, pageSize, i);
				subList = service.parseJsonToProducts(response);
				if(subList != null && subList.size() > 0){
					listTotal.addAll(subList);
				}
			}
		 
			service.updateProductsData(u, listTotal);
			
		}catch(Exception e){
			requestPool.remove(userIdStr);
			throw e;
		}
		
		List<Products> oldListTemp = service.getItemsByUserId(userId);
		
		List<Products> oldList = new ArrayList<Products>();
		
		if(oldListTemp != null){
			oldList.addAll(oldListTemp);
		}
		
		Map<String, List<Products> > map = service.splitProducts(listTotal, oldList);
		
		dto.setAddNum(service.insertItemBatch(map.get("add")));
		dto.setUpdateNum(service.updateItems(map.get("update")));
		dto.setDeleteNum(service.deleteItems(map.get("delete")));
		
		Date endProcessTime = new Date();
		long durTime = endProcessTime.getTime() - beginProcessTime.getTime();
		dto.setDurTime(durTime);
		
		requestPool.remove(userIdStr);
		
		return dto;
	}
	
}